package com.vip.mongofile.service.impl;

import cn.hutool.core.io.IoUtil;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.vip.common.response.ErrorCode;
import com.vip.common.response.Result;
import com.vip.common.response.Results;
import com.vip.common.util.EmptyUtils;
import com.vip.common.util.SnowflakeUtils;
import com.vip.mongofile.constant.FileStorageEnum;
import com.vip.mongofile.model.MongoFile;
import com.vip.mongofile.util.MD5Util;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.Binary;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.Date;
import java.util.Optional;

/**
 * @Description: Mongo文件实现层
 * @Date: 2020/12/03 17:30
 * @Author: wp
 **/
@Slf4j
@Service(value = "bigFileStorage")
@RequiredArgsConstructor
public class BigFileStorageServiceImpl extends AbstractFileStorageService {

    private final MongoTemplate mongoTemplate;
    private final GridFsTemplate gridFsTemplate;
    private final GridFSBucket gridfsbucket;

    @Override
    public Result<MongoFile> saveFile(MultipartFile file) {
        try {
            if (StringUtils.isEmpty(file.getOriginalFilename())) {
                throw new IllegalArgumentException("文件名不能为空");
            }
            String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
            MongoFile fileModel = new MongoFile(file.getOriginalFilename(), file.getContentType(), file.getSize(), suffix);
            fileModel.setUploadDate(new Date());
            String gridFsId = uploadFileToGridFs(file.getInputStream(), file.getContentType());
            fileModel.setMd5(MD5Util.getMD5(file.getInputStream()));
            fileModel.setGridFsId(gridFsId);
            fileModel = mongoTemplate.save(fileModel);
            return Results.newSuccessResult(fileModel);
        }catch (Exception e){
            log.error("大文件上传失败", e);
        }
        return Results.newFailResult(ErrorCode.SYSTEM_ERROR, "上传失败");
    }

    private String uploadFileToGridFs(InputStream in, String contentType) {
        String gridFsId = SnowflakeUtils.genStrId();
        log.info("上传GridFs生成gridFsId：{}", gridFsId);
        //将文件存储进GridFS中
        gridFsTemplate.store(in, gridFsId, contentType);
        return gridFsId;
    }

    @Override
    public Result<String> removeFile(String id) {
        try{
            //根据id查询文件
            MongoFile fileModel = mongoTemplate.findById(id, MongoFile.class);
            if (EmptyUtils.fieldsIsEmpty(fileModel)) {
                return Results.newFailResult(ErrorCode.DATA_NONE, "文件不存在");
            }
            //根据文件ID删除fs.files和fs.chunks中的记录
            Query deleteFileQuery = new Query().addCriteria(Criteria.where("filename").is(fileModel.getGridFsId()));
            gridFsTemplate.delete(deleteFileQuery);
            //删除集合fileDocument中的数据
            Query deleteQuery = new Query(Criteria.where("id").is(id));
            mongoTemplate.remove(deleteQuery, MongoFile.class);
            return Results.newSuccessResult(null, "删除成功");
        }catch (Exception e){
            log.error("大文件删除出错", e);
        }
        return Results.newFailResult(ErrorCode.SYSTEM_ERROR, "删除失败");
    }

    @Override
    public Result<MongoFile> getFileById(String mongoId) {
        try {
            MongoFile fileModel = mongoTemplate.findById(mongoId, MongoFile.class);
            if (fileModel == null) {
                return Results.newFailResult(ErrorCode.DATA_NONE, "文件不存在");
            }
            //根据文件ID查找fs.files和fs.chunks中的记录
            Query gridQuery = new Query().addCriteria(Criteria.where("filename").is(fileModel.getGridFsId()));
            //根据id查询文件
            Optional<GridFSFile> fsFileOptional = Optional.ofNullable(gridFsTemplate.findOne(gridQuery));
            if (!fsFileOptional.isPresent()) {
                return Results.newFailResult(ErrorCode.DATA_NONE, "文件不存在");
            }
            GridFSFile fsFile = fsFileOptional.get();
            //打开流下载对象
            GridFSDownloadStream in = gridfsbucket.openDownloadStream(fsFile.getObjectId());
            if (in.getGridFSFile().getLength() <= 0) {
                return Results.newFailResult(ErrorCode.DATA_NONE, "文件不存在");
            }
            //获取流对象
            GridFsResource resource = new GridFsResource(fsFile, in);
            //获取数据
            fileModel.setContent(new Binary(IoUtil.readBytes(resource.getInputStream())));
            return Results.newSuccessResult(fileModel);
        }catch (Exception e){
            log.error("文件查询出错", e);
        }
        return Results.newFailResult(ErrorCode.SYSTEM_ERROR, "查询失败");
    }

    @Override
    public FileStorageEnum getEnumType() {
        return FileStorageEnum.MONGO_BIG;
    }
}

